# from selenium.webdriver.common.by import By
# from selenium.webdriver.support.ui import WebDriverWait
# from selenium.webdriver.support import expected_conditions as EC
# from tools.file_read import get_absolute_path, load_yaml_data
#
#
# # ------------------------------
# # 1. 描述符类：封装元素操作（不变）
# # ------------------------------
# class SeleniumElementDescriptor:
#     def __init__(self, locator, wait_time=10, wait_condition=EC.element_to_be_clickable):
#         # self.locator = locator  # (By.XXX, 定位表达式)
#         # self.wait_time = wait_time
#         # self.wait_condition = wait_condition
#
#         if len(locator) == 3:
#             self.locator = (locator[0], locator[1])
#             self.wait_time = locator[2]
#         else:
#             self.locator = locator
#             self.wait_time = wait_time
#         self.wait_condition = wait_condition
#
#     def __get__(self, instance, owner_type=None):
#         if instance is None:
#             return self
#         # 等待并定位元素
#         wait = WebDriverWait(instance.driver, self.wait_time)
#         return wait.until(self.wait_condition(self.locator))
#
#     def __set__(self, instance, value):
#         # 输入框赋值（清空+输入）
#         element = self.__get__(instance)
#         element.clear()
#         element.send_keys(value)
#
#
# # ------------------------------
# # 2. 增强元类：自动识别模式（YAML / 手动）
# # ------------------------------
# class PageMeta(type):
#     def __new__(cls, name, bases, namespace):
#         # 标记：是否启用YAML模式
#         is_yaml_mode = False
#         # 存储定位器配置（用于后续生成描述符）
#         final_locators = {}
#
#         # ------------------------------
#         # 分支1：YAML驱动模式（页面类指定了yaml_config_path）
#         # ------------------------------
#         if "yaml_config_path" in namespace and namespace["yaml_config_path"]:
#             is_yaml_mode = True
#             try:
#                 # 1. 加载YAML配置文件
#                 yaml_abs_path = get_absolute_path(namespace["yaml_config_path"])
#                 page_config = load_yaml_data(yaml_abs_path)  # 复用之前的YAML读取工具
#
#                 # 2. 解析YAML中的定位器（转换为(By.XXX, 表达式)格式）
#                 yaml_locators = page_config.get("locators", {})
#                 print(f"这是解析出来的YAML数据：{yaml_locators}")
#                 for elem_name, loc_info in yaml_locators.items():
#                     # 解析定位方式（如 "By.ID" → By.ID）
#                     by_enum = getattr(By, loc_info[0].split(".")[-1], None)
#                     if not by_enum:
#                         raise ValueError(f"YAML定位方式错误：{loc_info[0]}（支持By.ID/By.CSS_SELECTOR等）")
#                     final_locators[elem_name] = (by_enum, loc_info[1])
#
#                 # 3. 解析YAML中的业务方法，动态生成（仅YAML模式需要）
#                 yaml_methods = page_config.get("business_methods", {})
#                 for method_name, method_config in yaml_methods.items():
#                     # 避免覆盖页面类手动定义的方法
#                     if method_name in namespace:
#                         continue
#
#                     # 生成动态业务方法（支持参数替换、步骤执行）
#                     def generate_dynamic_method(method_config=method_config):
#                         def method(instance, **kwargs):
#                             # 验证必填参数
#                             required_params = method_config.get("params", [])
#                             for param in required_params:
#                                 if param not in kwargs:
#                                     raise ValueError(f"方法[{method_name}]缺少参数：{param}")
#
#                             # 执行方法步骤
#                             steps = method_config.get("steps", [])
#                             return_value = None
#                             for step in steps:
#                                 action = step.get("action")
#                                 elem_name = step.get("element")
#                                 if not action or not elem_name:
#                                     raise ValueError(f"YAML步骤配置错误：{step}")
#
#                                 # 获取元素（通过描述符）
#                                 element = getattr(instance, elem_name)
#
#                                 # 执行操作（send_keys/click/get_text）
#                                 if action == "send_keys":
#                                     # 替换参数占位符（如 ${username} → kwargs["username"]）
#                                     value = step.get("value")
#                                     if isinstance(value, str) and value.startswith("${") and value.endswith("}"):
#                                         value = kwargs.get(value[2:-1])
#                                     element.send_keys(value)
#                                 elif action == "click":
#                                     element.click()
#                                 elif action == "get_text":
#                                     return_value = element.text
#
#                                 # 步骤返回值（如获取错误提示文本）
#                                 if step.get("return"):
#                                     return return_value
#                             return return_value
#
#                         # 给方法添加文档注释
#                         method.__doc__ = method_config.get("desc", f"YAML动态生成方法：{method_name}")
#                         return method
#
#                     # 将动态方法绑定到页面类
#                     namespace[method_name] = generate_dynamic_method()
#
#             except Exception as e:
#                 raise Exception(f"YAML模式初始化失败（{name}）：{str(e)}")
#
#         # ------------------------------
#         # 分支2：手动定义模式（页面类指定了locators字典）
#         # ------------------------------
#         elif "locators" in namespace and isinstance(namespace["locators"], dict):
#             # 直接使用页面类手动定义的locators
#             final_locators = namespace["locators"]
#
#         # ------------------------------
#         # 异常：两种模式都未配置
#         # ------------------------------
#         else:
#             raise ValueError(
#                 f"Page类[{name}]需配置以下任一模式：\n"
#                 "1. YAML驱动：指定 yaml_config_path 类属性（如 yaml_config_path = 'data/login_page_backup.yaml'）\n"
#                 "2. 手动定义：指定 locators 字典（如 locators = {'username_input': (By.ID, 'ls_username')}）"
#             )
#
#         # ------------------------------
#         # 通用逻辑：生成元素描述符（两种模式共用）
#         # ------------------------------
#         for elem_name, locator in final_locators.items():
#             # 避免覆盖页面类已有的属性（优先手动定义）
#             if elem_name not in namespace:
#                 namespace[elem_name] = SeleniumElementDescriptor(locator)
#
#         return super().__new__(cls, name, bases, namespace)
#
#
# # ------------------------------
# # 3. 基类：兼容双模式的入口
# # ------------------------------
# class BasePage(metaclass=PageMeta):
#     # YAML模式：子类需指定此属性（如 yaml_config_path = "data/login_page_backup.yaml"）
#     yaml_config_path = None
#     # 手动模式：子类需指定此字典（如 locators = {"play_btn": (By.ID, "play")}）
#     locators = {}
#
#     def __init__(self, driver):
#         self.driver = driver  # 接收conftest注入的driver
#
#     # 通用方法：两种模式共用
#     def go_to_url(self, url):
#         self.driver.get(url)
#
#     def get_title(self):
#         return self.driver.title
#
#     def get_current_url(self):
#         return self.driver.current_url

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from tools.file_read import get_absolute_path, load_yaml_data


# ------------------------------
# 通用工具函数：解析 ${xxx} 占位符
# ------------------------------
def resolve_placeholders(value: str, params: dict) -> str:
    """
    解析字符串中的 ${参数名} 占位符，替换为 params 中的实际值
    :param value: 可能包含占位符的字符串（如 "${login_success_good}"）
    :param params: 参数字典（如 {"login_success_good": "login_success_good"}）
    :return: 替换后的字符串
    """
    if isinstance(value, str) and value.startswith("${") and value.endswith("}"):
        param_key = value[2:-1]  # 提取占位符中的参数名（如 "login_success_good"）
        # return params.get(param_key, value)  # 替换为实际值，未找到则保留原字符串
        resolved_value = params.get(param_key, value)  # 从 params 中获取值
        # 可选：添加日志验证
        print(f"[Resolve] 占位符 {value} 解析为 {resolved_value}（参数集：{params.keys()}）")
        return resolved_value
    return value


# ------------------------------
# 1. 描述符类：封装元素操作（支持等待时间）
# ------------------------------
class SeleniumElementDescriptor:
    def __init__(self, locator, wait_time=10, wait_condition=EC.element_to_be_clickable):
        self.locator = locator  # (By.XXX, 定位表达式)
        self.wait_time = wait_time  # 元素等待时间（默认10秒）
        self.wait_condition = wait_condition  # 等待条件（默认元素可点击）

    def __get__(self, instance, owner_type=None):
        if instance is None:
            return self
        # 等待并定位元素（断言时会自动触发此等待）
        wait = WebDriverWait(instance.driver, self.wait_time)
        return wait.until(self.wait_condition(self.locator))

    def __set__(self, instance, value):
        # 输入框赋值（清空+输入）
        element = self.__get__(instance)
        element.clear()
        element.send_keys(value)


# ------------------------------
# 2. 增强元类：自动识别模式（YAML / 手动）
# ------------------------------
class PageMeta(type):
    def __new__(cls, name, bases, namespace):
        is_yaml_mode = False
        final_locators = {}

        # ------------------------------
        # 分支1：YAML驱动模式（页面类指定 yaml_config_path）
        # ------------------------------
        if "yaml_config_path" in namespace and namespace["yaml_config_path"]:
            is_yaml_mode = True
            try:
                # 加载YAML配置
                yaml_abs_path = get_absolute_path(namespace["yaml_config_path"])
                page_config = load_yaml_data(yaml_abs_path)

                # 解析定位器（转换为 By.XXX 格式）
                yaml_locators = page_config.get("locators", {})
                for elem_name, loc_info in yaml_locators.items():
                    # 解析定位方式（如 "By.CSS_SELECTOR" → By.CSS_SELECTOR）
                    by_enum = getattr(By, loc_info[0].split(".")[-1], None)
                    if not by_enum:
                        raise ValueError(f"YAML定位方式错误：{loc_info[0]}（支持By.ID/By.CSS_SELECTOR等）")
                    # 支持自定义等待时间（如 loc_info = ["By.CSS_SELECTOR", "#elem", 15]）
                    wait_time = loc_info[2] if len(loc_info) >= 3 else 10
                    final_locators[elem_name] = (by_enum, loc_info[1], wait_time)

                # 解析业务方法（动态生成）
                yaml_methods = page_config.get("business_methods", {})
                for method_name, method_config in yaml_methods.items():
                    if method_name in namespace:
                        continue  # 手动方法优先级高于动态方法

                    def generate_dynamic_method(method_config=method_config):
                        def method(instance, **kwargs):
                            # 1. 验证必填参数
                            required_params = method_config.get("params", [])
                            for param in required_params:
                                if param not in kwargs:
                                    raise ValueError(f"方法[{method_name}]缺少参数：{param}")

                            # 2. 执行步骤（支持占位符解析 + get_text 结果保存）
                            steps = method_config.get("steps", [])
                            return_value = None
                            for step in steps:
                                action = step.get("action")
                                elem_name = step.get("element")
                                if not action or not elem_name:
                                    raise ValueError(f"YAML步骤配置错误：{step}")

                                # 解析元素名占位符（如 "${login_success_good}" → "login_success_good"）
                                resolved_elem_name = resolve_placeholders(elem_name, kwargs)
                                # 获取元素（触发描述符的等待逻辑）
                                element = getattr(instance, resolved_elem_name)

                                # 3. 执行具体动作
                                if action == "send_keys":
                                    # 解析输入值占位符（如 "${username}" → "test_user"）
                                    value = resolve_placeholders(step.get("value"), kwargs)
                                    element.send_keys(value)
                                elif action == "click":
                                    element.click()
                                elif action == "get_text":
                                    # 保存文本结果到 kwargs（供后续断言使用）
                                    text = element.text
                                    var_name = step.get("var")  # YAML中配置的变量名（如 "welcome_text"）
                                    if var_name:
                                        # 去除首尾空格，避免断言时因空格失败
                                        kwargs[var_name] = text.strip() # 结果存入kwargs，断言时可引用
                                        # 可选：添加日志验证
                                        print(f"[PageBase] 保存变量 {var_name} = {kwargs[var_name]}")
                                    return_value = text  # 支持方法返回文本
                                    # return text
                                else:
                                    raise ValueError(f"不支持的动作类型：{action}（支持send_keys/click/get_text）")

                            return (return_value, kwargs)

                        method.__doc__ = method_config.get("desc", f"YAML动态方法：{method_name}")
                        return method

                    # 绑定动态方法到页面类
                    namespace[method_name] = generate_dynamic_method()

            except Exception as e:
                raise Exception(f"YAML模式初始化失败（{name}）：{str(e)}")

        # ------------------------------
        # 分支2：手动定义模式（页面类指定 locators 字典）
        # ------------------------------
        elif "locators" in namespace and isinstance(namespace["locators"], dict):
            final_locators = namespace["locators"]

        # ------------------------------
        # 异常：未配置任何模式
        # ------------------------------
        else:
            raise ValueError(
                f"Page类[{name}]需配置以下任一模式：\n"
                "1. YAML驱动：指定 yaml_config_path（如 yaml_config_path = 'data/login_page.yaml'）\n"
                "2. 手动定义：指定 locators 字典（如 locators = {'elem': (By.ID, 'id', 10)}）"
            )

        # ------------------------------
        # 通用逻辑：生成元素描述符（绑定到页面类）
        # ------------------------------
        for elem_name, loc_info in final_locators.items():
            if elem_name in namespace:
                continue
            # 解析定位器、等待时间、等待条件
            by_enum, loc_expr = loc_info[0], loc_info[1]
            wait_time = loc_info[2] if len(loc_info) >= 3 else 10
            wait_condition = EC.element_to_be_clickable  # 默认等待元素可点击
            # 生成描述符并绑定到页面类
            namespace[elem_name] = SeleniumElementDescriptor(
                locator=(by_enum, loc_expr),
                wait_time=wait_time,
                wait_condition=wait_condition
            )

        return super().__new__(cls, name, bases, namespace)


# ------------------------------
# 3. 页面基类（兼容双模式）
# ------------------------------
class BasePage(metaclass=PageMeta):
    yaml_config_path = None  # YAML模式：子类指定此属性
    locators = {}            # 手动模式：子类指定此属性

    def __init__(self, driver):
        self.driver = driver  # 接收外部传入的driver（线程隔离）
        self.wait_time = 10   # 默认全局等待时间（可被元素级等待时间覆盖）

    # 通用方法
    def go_to_url(self, url):
        self.driver.get(url)

    def get_current_url(self):
        return self.driver.current_url

    def get_title(self):
        return self.driver.title